/**
 * Copyright (C) 2010-2016 eBusiness Information, Excilys Group
 * Copyright (C) 2016-2020 the AndroidAnnotations project
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed To in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.ohosannotations.internal.process;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;

import org.ohosannotations.helper.CanonicalNameConstants;
import org.ohosannotations.holder.GeneratedClassHolder;

import com.helger.jcodemodel.AbstractJClass;
import com.helger.jcodemodel.JClassAlreadyExistsException;
import com.helger.jcodemodel.JCodeModel;
import com.helger.jcodemodel.JDefinedClass;

/**
 * 过程持有人
 *
 * @author dev
 * @since 2021-07-22
 */
public class ProcessHolder {
    // CHECKSTYLE:OFF

    /**
     * 类
     *
     * @author dev
     * @since 2021-07-22
     */
    public class Classes {
        // Java
        /**
         * 运行时异常
         */
        public final AbstractJClass RUNTIME_EXCEPTION = refClass(RuntimeException.class);
        /**
         * 异常
         */
        public final AbstractJClass EXCEPTION = refClass(Exception.class);
        /**
         * throwable
         */
        public final AbstractJClass THROWABLE = refClass(Throwable.class);
        /**
         * 字符序列
         */
        public final AbstractJClass CHAR_SEQUENCE = refClass(CharSequence.class);
        /**
         * 类转换异常
         */
        public final AbstractJClass CLASS_CAST_EXCEPTION = refClass(ClassCastException.class);
        /**
         * 可序列化的
         */
        public final AbstractJClass SERIALIZABLE = refClass(Serializable.class);
        /**
         * 字符串
         */
        public final AbstractJClass STRING = refClass(String.class);
        /**
         * 字符串生成器
         */
        public final AbstractJClass STRING_BUILDER = refClass(StringBuilder.class);
        /**
         * 系统
         */
        public final AbstractJClass SYSTEM = refClass(System.class);
        /**
         * 输入流
         */
        public final AbstractJClass INPUT_STREAM = refClass(InputStream.class);
        /**
         * 文件输入流
         */
        public final AbstractJClass FILE_INPUT_STREAM = refClass(FileInputStream.class);
        /**
         * sql异常
         */
        public final AbstractJClass SQL_EXCEPTION = refClass(SQLException.class);
        /**
         * 集合
         */
        public final AbstractJClass COLLECTIONS = refClass(Collections.class);
        /**
         * 线程
         */
        public final AbstractJClass THREAD = refClass(Thread.class);
        /**
         * 散列映射
         */
        public final AbstractJClass HASH_MAP = refClass(HashMap.class);
        /**
         * 列表
         */
        public final AbstractJClass LIST = refClass(List.class);
        /**
         * 对象
         */
        public final AbstractJClass OBJECT = refClass(Object.class);
        /**
         * 数组
         */
        public final AbstractJClass ARRAYS = refClass(Arrays.class);
        /**
         * 散列集
         */
        public final AbstractJClass HASH_SET = refClass(HashSet.class);
        /**
         * 类
         */
        public final AbstractJClass CLASS = refClass(Class.class);
        /**
         * 地图
         */
        public final AbstractJClass MAP = refClass(Map.class);

        /**
         * 嗨日志
         */
        public final AbstractJClass HI_LOG = refClass(CanonicalNameConstants.HI_LOG);
        /**
         * 日志
         */
        public final AbstractJClass LOG = refClass(CanonicalNameConstants.LOG);
        /**
         * 嗨日志标签
         */
        public final AbstractJClass HI_LOG_LABEL = refClass(CanonicalNameConstants.HI_LOG_LABEL);
        /**
         * 目的参数
         */
        public final AbstractJClass INTENT_PARAMS = refClass(CanonicalNameConstants.INTENT_PARAMS);
        /**
         * pacmap
         */
        public final AbstractJClass PACMAP = refClass(CanonicalNameConstants.PACMAP);
        /**
         * 能力
         */
        public final AbstractJClass ABILITY = refClass(CanonicalNameConstants.ABILITY);
        /**
         * 可编辑的
         */
        public final AbstractJClass EDITABLE = refClass(CanonicalNameConstants.EDITABLE);
        /**
         * 文本的观察者
         */
        public final AbstractJClass TEXT_OBSERVER = refClass(CanonicalNameConstants.TEXT_OBSERVER);
        /**
         * 滑块
         */
        public final AbstractJClass SLIDER = refClass(CanonicalNameConstants.SLIDER);
        /**
         * 在滑块改变监听器
         */
        public final AbstractJClass ON_SLIDER_CHANGE_LISTENER
            = refClass(CanonicalNameConstants.ON_SLIDER_CHANGE_LISTENER);
        /**
         * 文本视图
         */
        public final AbstractJClass TEXT_VIEW = refClass(CanonicalNameConstants.TEXT_VIEW);
        /**
         * 文本编辑器动作监听器
         */
        public final AbstractJClass TEXT_EDITOR_ACTION_LISTENER
            = refClass(CanonicalNameConstants.TEXT_EDITOR_ACTION_LISTENER);
        /**
         * abs按钮
         */
        public final AbstractJClass ABS_BUTTON = refClass(CanonicalNameConstants.ABS_BUTTON);
        /**
         * abs按钮检查状态改变侦听器
         */
        public final AbstractJClass ABS_BUTTON_ON_CHECKED_STATE_CHANGED_LISTENER
            = refClass(CanonicalNameConstants.ABS_BUTTON_ON_CHECKED_STATE_CHANGED_LISTENER);
        /**
         * 组件
         */
        public final AbstractJClass COMPONENT = refClass(CanonicalNameConstants.COMPONENT);
        /**
         * 组件点击监听器
         */
        public final AbstractJClass COMPONENT_CLICKED_LISTENER
            = refClass(CanonicalNameConstants.COMPONENT_CLICKED_LISTENER);
        /**
         * 在触摸侦听器组件
         */
        public final AbstractJClass COMPONENT_ON_TOUCH_LISTENER
            = refClass(CanonicalNameConstants.COMPONENT_ON_TOUCH_LISTENER);
        /**
         * 组件长点击监听器
         */
        public final AbstractJClass COMPONENT_LONG_CLICKED_LISTENER
            = refClass(CanonicalNameConstants.COMPONENT_LONG_CLICKED_LISTENER);
        /**
         * 组件的焦点更改侦听器
         */
        public final AbstractJClass COMPONENT_FOCUS_CHANGED_LISTENER
            = refClass(CanonicalNameConstants.COMPONENT_FOCUS_CHANGED_LISTENER);
        /**
         * 视图组布局参数
         */
        public final AbstractJClass VIEW_GROUP_LAYOUT_PARAMS
            = refClass(CanonicalNameConstants.VIEW_GROUP_LAYOUT_PARAMS);
        /**
         * 关键事件
         */
        public final AbstractJClass KEY_EVENT = refClass(CanonicalNameConstants.KEY_EVENT);
        /**
         * 上下文
         */
        public final AbstractJClass CONTEXT = refClass(CanonicalNameConstants.CONTEXT);
        /**
         * 意图
         */
        public final AbstractJClass INTENT = refClass(CanonicalNameConstants.INTENT);
        /**
         * 意图过滤器
         */
        public final AbstractJClass INTENT_FILTER = refClass(CanonicalNameConstants.INTENT_FILTER);
        /**
         * 广播接收器
         */
        public final AbstractJClass BROADCAST_RECEIVER = refClass(CanonicalNameConstants.BROADCAST_RECEIVER);
        /**
         * 公共事件数据
         */
        public final AbstractJClass COMMON_EVENT_DATA = refClass(CanonicalNameConstants.COMMON_EVENT_DATA);
        /**
         * 本地广播管理器
         */
        public final AbstractJClass LOCAL_BROADCAST_MANAGER
            = refClass(CanonicalNameConstants.LOCAL_BROADCAST_MANAGER);
        /**
         * 组件名称
         */
        public final AbstractJClass COMPONENT_NAME = refClass(CanonicalNameConstants.COMPONENT_NAME);
        /**
         * 组件容器
         */
        public final AbstractJClass COMPONENT_CONTAINER = refClass(CanonicalNameConstants.COMPONENT_CONTAINER);
        /**
         * 布局分散
         */
        public final AbstractJClass LAYOUT_SCATTER = refClass(CanonicalNameConstants.LAYOUT_SCATTER);
        /**
         * 片段的能力
         */
        public final AbstractJClass FRAGMENT_ABILITY = refClass(CanonicalNameConstants.FRACTION_ABILITY);
        /**
         * 分数
         */
        public final AbstractJClass FRACTION = refClass(CanonicalNameConstants.FRACTION);
        /**
         * 支持v4分数
         */
        public final AbstractJClass SUPPORT_V4_FRACTION = refClass(CanonicalNameConstants.SUPPORT_V4_FRACTION);
        /**
         * 超文本标记语言
         */
        public final AbstractJClass HTML = refClass(CanonicalNameConstants.HTML);
        /**
         * 窗口管理器布局配置
         */
        public final AbstractJClass WINDOW_MANAGER_LAYOUT_CONFIG
            = refClass(CanonicalNameConstants.WINDOW_MANAGER_LAYOUT_CONFIG);
        /**
         * 适配器的观点
         */
        public final AbstractJClass ADAPTER_VIEW = refClass(CanonicalNameConstants.ADAPTER_VIEW);
        /**
         * 列表容器
         */
        public final AbstractJClass LIST_CONTAINER = refClass(CanonicalNameConstants.LIST_CONTAINER);
        /**
         * 项目长点击监听器
         */
        public final AbstractJClass ITEM_LONG_CLICKED_LISTENER
            = refClass(CanonicalNameConstants.ITEM_LONG_CLICKED_LISTENER);
        /**
         * 项点击监听器
         */
        public final AbstractJClass ITEM_CLICKED_LISTENER
            = refClass(CanonicalNameConstants.ITEM_CLICKED_LISTENER);
        /**
         * 项目选择监听器
         */
        public final AbstractJClass ITEM_SELECTED_LISTENER
            = refClass(CanonicalNameConstants.ITEM_SELECTED_LISTENER);
        /**
         * 窗口
         */
        public final AbstractJClass WINDOW = refClass(CanonicalNameConstants.WINDOW);
        /**
         * 菜单项
         */
        public final AbstractJClass MENU_ITEM = refClass(CanonicalNameConstants.MENU_ITEM);
        /**
         * 菜单增压泵
         */
        public final AbstractJClass MENU_INFLATER = refClass(CanonicalNameConstants.MENU_INFLATER);
        /**
         * 菜单
         */
        public final AbstractJClass MENU = refClass(CanonicalNameConstants.MENU);
        /**
         * 动画跑龙套
         */
        public final AbstractJClass ANIMATION_UTILS = refClass(CanonicalNameConstants.ANIMATION_UTILS);
        /**
         * 资源
         */
        public final AbstractJClass RESOURCES = refClass(CanonicalNameConstants.RESOURCES);
        /**
         * 配置
         */
        public final AbstractJClass CONFIGURATION = refClass(CanonicalNameConstants.CONFIGURATION);
        /**
         * 运动事件
         */
        public final AbstractJClass MOTION_EVENT = refClass(CanonicalNameConstants.TOUCH_EVENT);
        /**
         * 处理程序
         */
        public final AbstractJClass HANDLER = refClass(CanonicalNameConstants.HANDLER);
        /**
         * 密钥存储库
         */
        public final AbstractJClass KEY_STORE = refClass(CanonicalNameConstants.KEY_STORE);
        /**
         * 视图服务器
         */
        public final AbstractJClass VIEW_SERVER = refClass(CanonicalNameConstants.VIEW_SERVER);
        /**
         * parcelable
         */
        public final AbstractJClass PARCELABLE = refClass(CanonicalNameConstants.SEQUENCEABLE);
        /**
         * 电影
         */
        public final AbstractJClass LOOPER = refClass(CanonicalNameConstants.LOOPER);
        /**
         * 电源管理器
         */
        public final AbstractJClass POWER_MANAGER = refClass(CanonicalNameConstants.POWER_MANAGER);
        /**
         * 权力运行的锁类型
         */
        public final AbstractJClass POWER_RUNNING_LOCK_TYPE
            = refClass(CanonicalNameConstants.POWER_RUNNING_LOCK_TYPE);
        /**
         * 电源状态
         */
        public final AbstractJClass POWER_STATE = refClass(CanonicalNameConstants.POWER_STATE);
        /**
         * 运行锁
         */
        public final AbstractJClass RUNNING_LOCK = refClass(CanonicalNameConstants.RUNNING_LOCK);
        /**
         * 构建版本
         */
        public final AbstractJClass BUILD_VERSION = refClass(CanonicalNameConstants.BUILD_VERSION);
        /**
         * 构建版本代码
         */
        public final AbstractJClass BUILD_VERSION_CODES
            = refClass(CanonicalNameConstants.BUILD_VERSION_CODES);
        /**
         * 兼容能力
         */
        public final AbstractJClass ABILITY_COMPAT = refClass(CanonicalNameConstants.ABILITY_COMPAT);
        /**
         * 环境兼容
         */
        public final AbstractJClass CONTEXT_COMPAT = refClass(CanonicalNameConstants.CONTEXT_COMPAT);
        /**
         * 应用程序的小部件管理器
         */
        public final AbstractJClass APP_WIDGET_MANAGER = refClass(CanonicalNameConstants.APP_WIDGET_MANAGER);
        /**
         * 视图寻呼机
         */
        public final AbstractJClass VIEW_PAGER = refClass(CanonicalNameConstants.VIEW_PAGER);
        /**
         * 页面改变监听器
         */
        public final AbstractJClass PAGE_CHANGE_LISTENER = refClass(CanonicalNameConstants.PAGE_CHANGE_LISTENER);
        /**
         * databasehelper
         */
        public final AbstractJClass DATABASEHELPER = refClass(CanonicalNameConstants.DATABASEHELPER);
        /**
         * 偏好
         */
        public final AbstractJClass PREFERENCE = refClass(CanonicalNameConstants.PREFERENCE);
        /**
         * 支持v7的偏好
         */
        public final AbstractJClass SUPPORT_V7_PREFERENCE = refClass(CanonicalNameConstants.SUPPORT_V7_PREFERENCE);
        /**
         * 偏好改变监听器
         */
        public final AbstractJClass PREFERENCE_CHANGE_LISTENER
            = refClass(CanonicalNameConstants.PREFERENCE_CHANGE_LISTENER);
        /**
         * 支持v7偏好改变侦听器
         */
        public final AbstractJClass SUPPORT_V7_PREFERENCE_CHANGE_LISTENER
            = refClass(CanonicalNameConstants.SUPPORT_V7_PREFERENCE_CHANGE_LISTENER);
        /**
         * 偏好点击监听器
         */
        public final AbstractJClass PREFERENCE_CLICK_LISTENER
            = refClass(CanonicalNameConstants.PREFERENCE_CLICK_LISTENER);
        /**
         * 支持v7偏好点击监听器
         */
        public final AbstractJClass SUPPORT_V7_PREFERENCE_CLICK_LISTENER
            = refClass(CanonicalNameConstants.SUPPORT_V7_PREFERENCE_CLICK_LISTENER);
        /**
         * 偏好能力头
         */
        public final AbstractJClass PREFERENCE_ABILITY_HEADER
            = refClass(CanonicalNameConstants.PREFERENCE_ABILITY_HEADER);
        /**
         * 视图的数据绑定
         */
        public final AbstractJClass VIEW_DATA_BINDING = refClass(CanonicalNameConstants.VIEW_DATA_BINDING);
        /**
         * 数据绑定跑龙套
         */
        public final AbstractJClass DATA_BINDING_UTIL = refClass(CanonicalNameConstants.DATA_BINDING_UTIL);
        /**
         * ohosx兼容能力
         */
        public final AbstractJClass OHOSX_ABILITY_COMPAT = refClass(CanonicalNameConstants.OHOSX_ABILITY_COMPAT);
        /**
         * ohosx分数
         */
        public final AbstractJClass OHOSX_FRACTION = refClass(CanonicalNameConstants.OHOSX_FRACTION);
        /**
         * ohosx片段的能力
         */
        public final AbstractJClass OHOSX_FRAGMENT_ABILITY = refClass(CanonicalNameConstants.OHOSX_FRAGMENT_ABILITY);
        /**
         * ohosx本地广播管理器
         */
        public final AbstractJClass OHOSX_LOCAL_BROADCAST_MANAGER
            = refClass(CanonicalNameConstants.OHOSX_LOCAL_BROADCAST_MANAGER);
        /**
         * ohosx环境兼容
         */
        public final AbstractJClass OHOSX_CONTEXT_COMPAT = refClass(CanonicalNameConstants.OHOSX_CONTEXT_COMPAT);
        /**
         * ohosx偏好
         */
        public final AbstractJClass OHOSX_PREFERENCE = refClass(CanonicalNameConstants.OHOSX_PREFERENCE);
        /**
         * ohosx偏好点击监听器
         */
        public final AbstractJClass OHOSX_PREFERENCE_CLICK_LISTENER
            = refClass(CanonicalNameConstants.OHOSX_PREFERENCE_CLICK_LISTENER);
        /**
         * ohosx偏好改变监听器
         */
        public final AbstractJClass OHOSX_PREFERENCE_CHANGE_LISTENER
            = refClass(CanonicalNameConstants.OHOSX_PREFERENCE_CHANGE_LISTENER);
        /**
         * ohosx视图寻呼机
         */
        public final AbstractJClass OHOSX_VIEW_PAGER = refClass(CanonicalNameConstants.OHOSX_VIEW_PAGER);
        /**
         * ohosx页面改变监听器
         */
        public final AbstractJClass OHOSX_PAGE_CHANGE_LISTENER
            = refClass(CanonicalNameConstants.OHOSX_PAGE_CHANGE_LISTENER);
        /**
         * ohosx视图数据绑定
         */
        public final AbstractJClass OHOSX_VIEW_DATA_BINDING = refClass(CanonicalNameConstants.OHOSX_VIEW_DATA_BINDING);
        /**
         * ohosx数据绑定跑龙套
         */
        public final AbstractJClass OHOSX_DATA_BINDING_UTIL = refClass(CanonicalNameConstants.OHOSX_DATA_BINDING_UTIL);

        /**
         * 客户端连接管理器
         */
        public final AbstractJClass CLIENT_CONNECTION_MANAGER
            = refClass(CanonicalNameConstants.CLIENT_CONNECTION_MANAGER);
        /**
         * 缺省http客户端
         */
        public final AbstractJClass DEFAULT_HTTP_CLIENT = refClass(CanonicalNameConstants.DEFAULT_HTTP_CLIENT);
        /**
         * ssl套接字工厂
         */
        public final AbstractJClass SSL_SOCKET_FACTORY = refClass(CanonicalNameConstants.SSL_SOCKET_FACTORY);
        /**
         * 普通的套接字工厂
         */
        public final AbstractJClass PLAIN_SOCKET_FACTORY = refClass(CanonicalNameConstants.PLAIN_SOCKET_FACTORY);
        /**
         * 计划
         */
        public final AbstractJClass SCHEME = refClass(CanonicalNameConstants.SCHEME);
        /**
         * 计划的注册表
         */
        public final AbstractJClass SCHEME_REGISTRY = refClass(CanonicalNameConstants.SCHEME_REGISTRY);
        /**
         * SINGLE_CLIENT_CONN_MANAGER
         */
        public final AbstractJClass SINGLE_CLIENT_CONN_MANAGER
            = refClass(CanonicalNameConstants.SINGLE_CLIENT_CONN_MANAGER);
    }

    // CHECKSTYLE:ON

    private final Map<Element, GeneratedClassHolder> generatedClassHolders = new HashMap<>();

    private final ProcessingEnvironment processingEnvironment;

    private final JCodeModel codeModel;

    private final Map<String, AbstractJClass> loadedClasses = new HashMap<>();

    private final Classes classes;

    private final OriginatingElements originatingElements = new OriginatingElements();

    /**
     * ProcessHolder
     *
     * @param processingEnvironment processingEnvironment
     */
    public ProcessHolder(ProcessingEnvironment processingEnvironment) {
        this.processingEnvironment = processingEnvironment;
        codeModel = new JCodeModel();
        codeModel.addDontImportClass(refClass("ohos.R"));
        classes = new Classes();
    }

    /**
     * put
     *
     * @param element element
     * @param generatedClassHolder generatedClassHolder
     */
    public void put(Element element, GeneratedClassHolder generatedClassHolder) {
        JDefinedClass generatedClass = generatedClassHolder.getGeneratedClass();

        String qualifiedName = generatedClass.fullName();

        originatingElements.add(qualifiedName, element);

        generatedClassHolders.put(element, generatedClassHolder);
    }

    /**
     * getGeneratedClassHolder
     *
     * @param element element
     * @return generatedClassHolders
     */
    public GeneratedClassHolder getGeneratedClassHolder(Element element) {
        for (Map.Entry<Element, GeneratedClassHolder> entry : generatedClassHolders.entrySet()) {
            if (entry.getKey().asType().toString().equals(element.asType().toString())) {
                return generatedClassHolders.get(entry.getKey());
            }
        }
        return null;
    }

    /**
     * refClass
     *
     * @param clazz clazz
     * @return referencedClass
     */
    public AbstractJClass refClass(Class<?> clazz) {
        AbstractJClass referencedClass = codeModel.ref(clazz);
        loadedClasses.put(clazz.getCanonicalName(), referencedClass);
        return referencedClass;
    }

    /**
     * referencedClass
     *
     * @param fullyQualifiedClassName fullyQualifiedClassName
     * @return refClass
     */
    public AbstractJClass refClass(String fullyQualifiedClassName) {
        int arrayCounter = 0;
        while (fullyQualifiedClassName.endsWith("[]")) {
            arrayCounter++;
            fullyQualifiedClassName = fullyQualifiedClassName.substring(0, fullyQualifiedClassName.length() - 2);
        }
        AbstractJClass refClass = loadedClasses.get(fullyQualifiedClassName);
        if (refClass == null) {
            refClass = codeModel.directClass(fullyQualifiedClassName);
            loadedClasses.put(fullyQualifiedClassName, refClass);
        }
        for (int i = 0; i < arrayCounter; i++) {
            refClass = refClass.array();
        }
        return refClass;
    }

    /**
     * definedClass
     *
     * @param fullyQualifiedClassName fullyQualifiedClassName
     * @return refClass
     */
    public JDefinedClass definedClass(String fullyQualifiedClassName) {
        JDefinedClass refClass = (JDefinedClass) loadedClasses.get(fullyQualifiedClassName);
        if (refClass == null) {
            try {
                refClass = codeModel._class(fullyQualifiedClassName);
            } catch (JClassAlreadyExistsException e) {
                refClass = (JDefinedClass) refClass(fullyQualifiedClassName);
            }
            loadedClasses.put(fullyQualifiedClassName, refClass);
        }
        return refClass;
    }

    /**
     * processingEnvironment
     *
     * @return processingEnvironment
     */
    public ProcessingEnvironment processingEnvironment() {
        return processingEnvironment;
    }

    /**
     * codeModel
     *
     * @return codeModel
     */
    public JCodeModel codeModel() {
        return codeModel;
    }

    /**
     * classes
     *
     * @return classes
     */
    public Classes classes() {
        return classes;
    }

    /**
     * getOriginatingElements
     *
     * @return originatingElements
     */
    public OriginatingElements getOriginatingElements() {
        return originatingElements;
    }
}
